/*
 ------------------------------------------------------------------------------
 Copyright (C) 2006-2007 Team Blur.

 This file is part of the Quantum Engine source code.

 The Quantum Engine source code is free software; you can redistribute it and/or
 modify it under the terms of the GNU General Public License as published by
 the Free Software Foundation; either version 2 of the License, or (at your
 option) any later version.

 The Quantum Engine source code is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 more details.

 You should have received a copy of the GNU General Public License along with
 the Quantum Engine source code; if not, write to the Free Software Foundation, Inc.,
 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 ------------------------------------------------------------------------------
*/


#include "cg_local.h"


/*
 ==================
 CG_ParseDeltaPlayerState
 ==================
*/
static void CG_ParseDeltaPlayerState (playerState_t *oldState, playerState_t *newState, ByteBuffer *msg){

	playerState_t	nullState;

	if (!oldState){
		oldState = &nullState;
		memset(oldState, 0, sizeof(playerState_t));
	}

	newState->clientNum = msg->ReadByte();

	newState->origin.x = msg->ReadDeltaFloat(oldState->origin.x);
	newState->origin.y = msg->ReadDeltaFloat(oldState->origin.y);
	newState->origin.z = msg->ReadDeltaFloat(oldState->origin.z);

	newState->velocity.x = msg->ReadDeltaFloat(oldState->velocity.x);
	newState->velocity.y = msg->ReadDeltaFloat(oldState->velocity.y);
	newState->velocity.z = msg->ReadDeltaFloat(oldState->velocity.z);

	newState->angles.pitch = msg->ReadDeltaAngle16(oldState->angles.pitch);
	newState->angles.yaw = msg->ReadDeltaAngle16(oldState->angles.yaw);
	newState->angles.roll = msg->ReadDeltaAngle16(oldState->angles.roll);

	newState->deltaAngles[0] = msg->ReadDeltaShort(oldState->deltaAngles[0]);
	newState->deltaAngles[1] = msg->ReadDeltaShort(oldState->deltaAngles[1]);
	newState->deltaAngles[2] = msg->ReadDeltaShort(oldState->deltaAngles[2]);
}

/*
 ==================
 CG_ParseDeltaEntityState
 ==================
*/
static void CG_ParseDeltaEntityState (entityState_t *oldState, entityState_t *newState, int number, bool unchanged, ByteBuffer *msg){

	// Check for a remove
	if (msg->ReadBits(1) == 1){
		memset(newState, 0, sizeof(entityState_t));
		newState->number = ENTITYNUM_NONE;

		return;
	}

	// Check for no delta
	if (msg->ReadBits(1) == 0){
		*newState = *oldState;
		newState->number = number;

		return;
	}

	// Delta uncompress the entity state
	newState->number = number;

	newState->type = (entityType_t)msg->ReadByte();

	if (newState->type == ET_LIGHT){
		newState->origin.x = msg->ReadDeltaFloat(oldState->origin.x);
		newState->origin.y = msg->ReadDeltaFloat(oldState->origin.y);
		newState->origin.z = msg->ReadDeltaFloat(oldState->origin.z);
		newState->center.x = msg->ReadDeltaFloat(oldState->center.x);
		newState->center.y = msg->ReadDeltaFloat(oldState->center.y);
		newState->center.z = msg->ReadDeltaFloat(oldState->center.z);
		newState->angles.pitch = msg->ReadDeltaAngle16(oldState->angles.pitch);
		newState->angles.yaw = msg->ReadDeltaAngle16(oldState->angles.yaw);
		newState->angles.roll = msg->ReadDeltaAngle16(oldState->angles.roll);
		newState->radius.x = msg->ReadDeltaFloat(oldState->radius.x);
		newState->radius.y = msg->ReadDeltaFloat(oldState->radius.y);
		newState->radius.z = msg->ReadDeltaFloat(oldState->radius.z);
		newState->fovX = msg->ReadDeltaFloat(oldState->fovX);
		newState->fovY = msg->ReadDeltaFloat(oldState->fovY);
		newState->falloffRange = msg->ReadDeltaFloat(oldState->falloffRange);
		newState->projected = msg->ReadBool();
		newState->parallel = msg->ReadBool();
		newState->noShadows = msg->ReadBool();
		newState->detailLevel = msg->ReadDeltaLong(oldState->detailLevel);
		newState->materialIndex = msg->ReadDeltaLong(oldState->materialIndex);
		newState->materialParms[0] = msg->ReadDeltaFloat(oldState->materialParms[0]);
		newState->materialParms[1] = msg->ReadDeltaFloat(oldState->materialParms[1]);
		newState->materialParms[2] = msg->ReadDeltaFloat(oldState->materialParms[2]);
		newState->materialParms[3] = msg->ReadDeltaFloat(oldState->materialParms[3]);
		newState->materialParms[4] = msg->ReadDeltaFloat(oldState->materialParms[4]);
		newState->materialParms[5] = msg->ReadDeltaFloat(oldState->materialParms[5]);
		newState->materialParms[6] = msg->ReadDeltaFloat(oldState->materialParms[6]);
		newState->materialParms[7] = msg->ReadDeltaFloat(oldState->materialParms[7]);
	}
	else {
		newState->flags = msg->ReadDeltaLong(oldState->flags);
		newState->origin.x = msg->ReadDeltaFloat(oldState->origin.x);
		newState->origin.y = msg->ReadDeltaFloat(oldState->origin.y);
		newState->origin.z = msg->ReadDeltaFloat(oldState->origin.z);
		newState->angles.pitch = msg->ReadDeltaAngle16(oldState->angles.pitch);
		newState->angles.yaw = msg->ReadDeltaAngle16(oldState->angles.yaw);
		newState->angles.roll = msg->ReadDeltaAngle16(oldState->angles.roll);
		newState->modelIndex = msg->ReadDeltaLong(oldState->modelIndex);
		newState->clipModelIndex = msg->ReadDeltaLong(oldState->clipModelIndex);
		newState->materialIndex = msg->ReadDeltaLong(oldState->materialIndex);
		newState->soundShaderIndex = msg->ReadDeltaLong(oldState->soundShaderIndex);
		newState->materialParms[0] = msg->ReadDeltaFloat(oldState->materialParms[0]);
		newState->materialParms[1] = msg->ReadDeltaFloat(oldState->materialParms[1]);
		newState->materialParms[2] = msg->ReadDeltaFloat(oldState->materialParms[2]);
		newState->materialParms[3] = msg->ReadDeltaFloat(oldState->materialParms[3]);
		newState->materialParms[4] = msg->ReadDeltaFloat(oldState->materialParms[4]);
		newState->materialParms[5] = msg->ReadDeltaFloat(oldState->materialParms[5]);
		newState->materialParms[6] = msg->ReadDeltaFloat(oldState->materialParms[6]);
		newState->materialParms[7] = msg->ReadDeltaFloat(oldState->materialParms[7]);
		newState->solidBox = msg->ReadDeltaLong(oldState->solidBox);
		newState->solidContents = msg->ReadDeltaLong(oldState->solidContents);
	}
}

/*
 ==================
 CG_DeltaEntity
 ==================
*/
static void CG_DeltaEntity (snapshot_t *snapshot, entityState_t *oldState, int number, bool unchanged, ByteBuffer *msg){

	entityState_t	*newState;

	newState = &cgs.parseEntities[cgs.parseEntitiesNum & (MAX_PARSE_ENTITIES - 1)];

	if (unchanged)
		*newState = *oldState;
	else
		CG_ParseDeltaEntityState(oldState, newState, number, unchanged, msg);

	// Check if the entity was delta removed
	if (newState->number == ENTITYNUM_NONE){
		if (cg_showDeltaEntities->integerValue)
			odSystem->Printf("%6i: remove: %i\n", number);

		// TODO: do this elsewhere
		if (cg.entities[number].entityHandle != -1){
			odRenderer->FreeRenderEntity(cgs.scene, cg.entities[number].entityHandle);
			cg.entities[number].entityHandle = -1;
		}
		if (cg.entities[number].lightHandle != -1){
			odRenderer->FreeRenderLight(cgs.scene, cg.entities[number].lightHandle);
			cg.entities[number].lightHandle = -1;
		}
		if (cg.entities[number].emitterHandle != -1){
			odSound->FreeSoundEmitter(cg.entities[number].emitterHandle, true);
			cg.entities[number].emitterHandle = -1;
		}

		return;
	}

	cgs.parseEntitiesNum++;

	snapshot->numEntities++;
}

/*
 ==================
 CG_ParseSnapshotEntities
 ==================
*/
static void CG_ParseSnapshotEntities (snapshot_t *oldSnapshot, snapshot_t *newSnapshot, ByteBuffer *msg){

	entityState_t	*oldState = NULL;
	int				oldIndex = 0;
	int				oldNum, newNum;

	newSnapshot->numEntities = 0;
	newSnapshot->parseEntitiesNum = cgs.parseEntitiesNum;

	// Delta from the entities present in oldSnapshot
	if (!oldSnapshot)
		oldNum = 9999;
	else {
		if (oldIndex >= oldSnapshot->numEntities)
			oldNum = 9999;
		else {
			oldState = &cgs.parseEntities[(oldSnapshot->parseEntitiesNum + oldIndex) & (MAX_PARSE_ENTITIES - 1)];
			oldNum = oldState->number;
		}
	}

	while (1){
		// Read the entity index number
		newNum = msg->ReadBits(ENTITYNUM_BITS);

		if (newNum == ENTITYNUM_NONE)
			break;

		if (msg->getBitsRead() > msg->getBitsWritten())
			odSystem->Error(false, "CG_ParseSnapshotEntities: end of message");

		while (oldNum < newNum){
			// One or more entities from the old snapshot are unchanged
			if (cg_showDeltaEntities->integerValue)
				odSystem->Printf("%6i: unchanged: %i\n", msg->getBitsRead(), oldNum);

			CG_DeltaEntity(newSnapshot, oldState, oldNum, true, msg);

			oldIndex++;

			if (oldIndex >= oldSnapshot->numEntities)
				oldNum = 9999;
			else {
				oldState = &cgs.parseEntities[(oldSnapshot->parseEntitiesNum + oldIndex) & (MAX_PARSE_ENTITIES - 1)];
				oldNum = oldState->number;
			}
		}

		if (oldNum == newNum){
			// Delta from previous state
			if (cg_showDeltaEntities->integerValue)
				odSystem->Printf("%6i: delta: %i\n", msg->getBitsRead(), newNum);

			CG_DeltaEntity(newSnapshot, oldState, newNum, false, msg);

			oldIndex++;

			if (oldIndex >= oldSnapshot->numEntities)
				oldNum = 9999;
			else {
				oldState = &cgs.parseEntities[(oldSnapshot->parseEntitiesNum + oldIndex) & (MAX_PARSE_ENTITIES - 1)];
				oldNum = oldState->number;
			}

			continue;
		}

		if (oldNum > newNum){
			// Delta from baseline
			if (cg_showDeltaEntities->integerValue)
				odSystem->Printf("%6i: baseline: %i\n", msg->getBitsRead(), newNum);

			CG_DeltaEntity(newSnapshot, &cgs.baselines[newNum], newNum, false, msg);

			continue;
		}
	}

	// Any remaining entities in the old snapshot are copied over
	while (oldNum != 9999){
		if (cg_showDeltaEntities->integerValue)
			odSystem->Printf("%6i: unchanged: %i\n", msg->getBitsRead(), oldNum);

		CG_DeltaEntity(newSnapshot, oldState, oldNum, true, msg);

		oldIndex++;

		if (oldIndex >= oldSnapshot->numEntities)
			oldNum = 9999;
		else {
			oldState = &cgs.parseEntities[(oldSnapshot->parseEntitiesNum + oldIndex) & (MAX_PARSE_ENTITIES - 1)];
			oldNum = oldState->number;
		}
	}
}

/*
 ==================
 CG_ParseSnapshot
 ==================
*/
bool CG_ParseSnapshot (int sequence, ByteBuffer *msg, int *time, bool *deltaCompressed){

	snapshot_t	*oldSnapshot, newSnapshot;
	byte		buffer[10];//;BSP_MAX_PORTALS/8];
	int			bytes;
	int			i;

	// Parse the snapshot header
	newSnapshot.valid = false;
	newSnapshot.time = msg->ReadLong();
	newSnapshot.ping = msg->ReadShort();
	newSnapshot.rateDelayed = msg->ReadBool();
	newSnapshot.deltaMessage = msg->ReadLong();
	newSnapshot.messageSequence = sequence;

	// If the snapshot is delta compressed from data that we no longer have
	// available, we must suck up the rest of the snapshot, but not use it,
	// then ask for an uncompressed snapshot
	if (newSnapshot.deltaMessage <= 0){
		// Uncompressed snapshot
		oldSnapshot = NULL;

		newSnapshot.valid = true;
	}
	else {
		oldSnapshot = &cgs.snapshots[newSnapshot.deltaMessage & PACKET_MASK];

		if (!oldSnapshot->valid){
			// Should never happen
			odSystem->Printf(S_COLOR_YELLOW "Delta from invalid snapshot (not supposed to happen!)\n");
		}
		else if (oldSnapshot->messageSequence != newSnapshot.deltaMessage){
			// The snapshot that the server did the delta from is too old, so
			// we can't reconstruct it properly
			odSystem->Printf(S_COLOR_YELLOW "Delta snapshot too old\n");
		}
		else if (cgs.parseEntitiesNum - oldSnapshot->parseEntitiesNum > MAX_PARSE_ENTITIES - MAX_SNAPSHOT_ENTITIES){
			odSystem->Printf(S_COLOR_YELLOW "Delta parseEntitiesNum too old\n");
		}
		else {
			// Valid delta parse
			newSnapshot.valid = true;
		}
	}

	// Parse the portal bits
	bytes = msg->ReadByte();
	msg->ReadData(buffer, bytes);

	// Set portal states
	for (i = 0; i < odCollision->NumPortals(); i++){
		if (buffer[i >> 3] & BIT(i & 7))
			odCollision->SetPortalState(i, true);
		else
			odCollision->SetPortalState(i, false);
	}

	// Parse the player state
	if (oldSnapshot)
		CG_ParseDeltaPlayerState(&oldSnapshot->state, &newSnapshot.state, msg);
	else
		CG_ParseDeltaPlayerState(NULL, &newSnapshot.state, msg);

	// Parse the entities
	CG_ParseSnapshotEntities(oldSnapshot, &newSnapshot, msg);

	// If not valid, dump the entire thing now that it has been properly read
	if (!newSnapshot.valid)
		return false;

	// Clear the valid flags of any snapshots between the last received and
	// this one, so if there was a dropped packet it won't look like something
	// valid to delta from next time we wrap around in the buffer
	i = sequence + 1;

	if (sequence - i >= PACKET_BACKUP)
		i = sequence - (PACKET_BACKUP - 1);

	for ( ; i < sequence; i++)
		cgs.snapshots[i & PACKET_MASK].valid = false;

	// Copy the new snapshot
	cgs.snapshot = newSnapshot;

	// Save the snapshot in the backup array for later delta comparisons
	cgs.snapshots[sequence & PACKET_MASK] = newSnapshot;

	// Set time and deltaCompressed
	*time = newSnapshot.time;
	*deltaCompressed = (newSnapshot.deltaMessage > 0);



	// TODO!!!
	if (!cg.firstSnapshot)
		cg.firstSnapshot = true;

	cgi->SetDeltaAngles(newSnapshot.state.deltaAngles);

	cg.renderView.origin = newSnapshot.state.origin;

	cg.velocity = newSnapshot.state.velocity;

	cg.renderViewAngles = newSnapshot.state.angles;
	cg.renderView.axis = cg.renderViewAngles.ToMat3();

	return true;
}
